Skip to content

Fix CPU / thread locks + improve performance of BrowserBookmark plugin #3815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from

Conversation

dcog989
Copy link
Contributor

@dcog989 dcog989 commented Jul 10, 2025

1. High CPU and UI Freezing

  • Initial Load Spike: The original plugin caused a massive CPU spike on first load due to inefficient database queries for favicons. Fix: We now create temporary indexes on the database copies, making the initial favicon scan virtually instantaneous.
  • Query-Time CPU Usage: Typing in the search bar caused high CPU because the plugin checked for the existence of every icon file on every keystroke. Fix: We now pre-validate all icon paths once during the initial load, making search queries extremely fast, in-memory operations.
  • Background CPU Churn: Actively browsing the web would trigger constant, high-CPU reloads of the plugin's data. Fix: We implemented a debouncing mechanism for the file watcher, ensuring that even a storm of file changes only results in a single, efficient data reload.
  • UI Freezing on Scroll (The Final Bug): The most severe issue was the UI locking up with 100% CPU on multiple threads when scrolling through results. This was caused by the UI's renderer (SharpVectors) attempting to render corrupt SVG favicons created by a race condition. Fix: We now convert all favicons to PNG format during the loading process. This guarantees that the UI never receives a corrupt or incompatible image, permanently solving the freezing issue.

2. Improved Reliability and Functionality

  • Fixed Firefox Profile Discovery: The plugin now reliably and automatically discovers the default Firefox profile, even in non-standard locations or with different naming schemes, by correctly parsing the profiles.ini file.
  • Fixed Real-Time Settings Changes: Toggling the "Load favicons" checkbox now triggers an immediate data reload, so the change takes effect instantly without requiring an application restart.

3. Enhanced Code Quality and User Experience

  • Asynchronous Startup: The entire data loading process is now fully asynchronous. Flow will show loading bar while bookmarks are loading in the background, ensuring the Flow Launcher UI remains responsive at all times.
  • Code Stability: eliminated unsafe async void methods, fixed various compilation errors, and centralized duplicated code into a shared helper class, making the plugin more stable and maintainable.

## 1. High CPU and UI Freezing

*   **Initial Load Spike:** The original plugin caused a massive CPU spike on first load due to inefficient database queries for favicons. **Fix:** We now create temporary indexes on the database copies, making the initial favicon scan virtually instantaneous.
*   **Query-Time CPU Usage:** Typing in the search bar caused high CPU because the plugin checked for the existence of every icon file on every keystroke. **Fix:** We now pre-validate all icon paths once during the initial load, making search queries extremely fast, in-memory operations.
*   **Background CPU Churn:** Actively browsing the web would trigger constant, high-CPU reloads of the plugin's data. **Fix:** We implemented a debouncing mechanism for the file watcher, ensuring that even a storm of file changes only results in a single, efficient data reload.
*   **UI Freezing on Scroll (The Final Bug):** The most severe issue was the UI locking up with 100% CPU on multiple threads when scrolling through results. This was caused by the UI's renderer (SharpVectors) attempting to render corrupt SVG favicons created by a race condition. **Fix:** We now convert **all** favicons to PNG format during the loading process. This guarantees that the UI never receives a corrupt or incompatible image, permanently solving the freezing issue.

## 2. Improved Reliability and Functionality

*   **Fixed Firefox Profile Discovery:** The plugin now reliably and automatically discovers the default Firefox profile, even in non-standard locations or with different naming schemes, by correctly parsing the `profiles.ini` file.
*   **Fixed Real-Time Settings Changes:** Toggling the "Load favicons" checkbox now triggers an immediate data reload, so the change takes effect instantly without requiring an application restart.

## 3. Enhanced Code Quality and User Experience

*   **Asynchronous Startup:** The entire data loading process is now fully asynchronous. The plugin displays an "initializing..." message while loading in the background, ensuring the Flow Launcher UI remains responsive at all times.
*   **Code Stability:** eliminated unsafe `async void` methods, fixed various compilation errors, and centralized duplicated code into a shared helper class, making the plugin more stable and maintainable.
@prlabeler prlabeler bot added the bug Something isn't working label Jul 10, 2025
Copy link

gitstream-cm bot commented Jul 10, 2025

🥷 Code experts: Jack251970, onesounds

Jack251970, onesounds have most 👩‍💻 activity in the files.
onesounds, Jack251970 have most 🧠 knowledge in the files.

See details

Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 71 additions & 97 deletions
MAY 11 additions & 1 deletions
APR 81 additions & 92 deletions
MAR 17 additions & 7 deletions 183 additions & 18 deletions
FEB

Knowledge based on git-blame:
Jack251970: 40%
onesounds: 18%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 258 additions & 198 deletions
MAY
APR 55 additions & 60 deletions
MAR 32 additions & 12 deletions 407 additions & 261 deletions
FEB

Knowledge based on git-blame:
Jack251970: 58%
onesounds: 22%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 35 additions & 34 deletions
MAY
APR 2 additions & 2 deletions
MAR 35 additions & 35 deletions 6 additions & 3 deletions
FEB

Knowledge based on git-blame:
Jack251970: 34%
onesounds: 1%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 76 additions & 0 deletions
MAY
APR
MAR
FEB

Knowledge based on git-blame:
Jack251970: 100%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 6 additions & 6 deletions
MAY 3 additions & 2 deletions
APR 17 additions & 17 deletions
MAR 17 additions & 18 deletions 16 additions & 2 deletions
FEB

Knowledge based on git-blame:
Jack251970: 14%
onesounds: 1%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN
MAY
APR 18 additions & 18 deletions
MAR
FEB

Knowledge based on git-blame:
onesounds: 78%
Jack251970: 9%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 10 additions & 2 deletions
MAY
APR
MAR 3 additions & 3 deletions
FEB 78 additions & 68 deletions

Knowledge based on git-blame:
Jack251970: 83%
onesounds: 11%

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs

Activity based on git-commit:

Jack251970 onesounds
JUL
JUN 5 additions & 6 deletions
MAY
APR 2 additions & 7 deletions
MAR 75 additions & 71 deletions
FEB

Knowledge based on git-blame:
onesounds: 10%
Jack251970: 6%

✨ Comment /gs review for LinearB AI review. Learn how to automate it here.

Copy link

gitstream-cm bot commented Jul 10, 2025

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

@coderabbitai coderabbitai bot added the enhancement New feature or request label Jul 10, 2025
@dcog989
Copy link
Contributor Author

dcog989 commented Jul 10, 2025

Fixes #3748 and probably #3731

Copy link
Contributor

coderabbitai bot commented Jul 10, 2025

📝 Walkthrough
## Walkthrough

This update refactors and improves the bookmark and favicon loading logic for Chromium and Firefox browsers in the BrowserBookmark plugin. It introduces robust error handling, centralized favicon processing with image resizing and SVG support, improved concurrency and initialization control, and enhanced file monitoring with debounced reloads. Settings and UI bindings are also streamlined.

## Changes

| File(s)                                                                                     | Change Summary |
|---------------------------------------------------------------------------------------------|---------------|
| .../ChromiumBookmarkLoader.cs                                                               | Added error handling for JSON parsing; refined bookmark type checks; refactored favicon loading to use a new helper; removed unused usings. |
| .../FirefoxBookmarkLoader.cs                                                                | Rewrote profile path parsing; replaced LINQ with explicit loops; consolidated favicon loading via helper; removed extension method and unused usings. |
| .../Flow.Launcher.Plugin.BrowserBookmark.csproj                                             | Upgraded Microsoft.Data.Sqlite package; added SixLabors.ImageSharp, SkiaSharp, and Svg.Skia package references. |
| .../Helper/FaviconHelper.cs                                                                 | Renamed and refactored temp DB handling method; added SVG detection and conversion to PNG; enhanced image resizing and error handling; removed old SVG detection method. |
| .../Main.cs                                                                                 | Refactored initialization and reload logic with async, thread-safe handling; improved file watching with debounce; updated query and dispose logic. |
| .../Views/CustomBrowserSetting.xaml                                                         | Removed a guide message TextBlock from the UI. |
| .../Views/SettingsControl.xaml                                                              | Changed CheckBox binding for "Enable Favicons" to bind directly to `EnableFavicons`. |
| .../Views/SettingsControl.xaml.cs                                                           | Added `EnableFavicons` property; simplified async calls; updated event handling for settings changes; removed unnecessary imports. |
| .../CustomChromiumBookmarkLoader.cs                                                         | Added missing `using System.IO;` directive. |
| .../Languages/*.xaml                                                                         | Added new localization string key `flowlauncher_plugin_browserbookmark_plugin_initializing` with translations in multiple languages. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant Main
    participant BookmarkLoader
    participant FaviconHelper
    participant FileSystem

    User->>Main: Start plugin / change settings
    Main->>Main: Acquire initialization semaphore
    Main->>BookmarkLoader: Load bookmarks (async)
    BookmarkLoader->>FaviconHelper: ProcessFavicons(bookmarks, db, ...)
    FaviconHelper->>FaviconHelper: Extract, resize, save favicons
    FaviconHelper-->>BookmarkLoader: Update bookmark favicon paths
    BookmarkLoader-->>Main: Return bookmarks
    Main->>Main: Set initialized flag

    FileSystem-->>Main: Bookmark file changed event
    Main->>Main: Debounce reload (3s)
    Main->>BookmarkLoader: Reload bookmarks and favicons

Possibly related issues

Possibly related PRs

Suggested reviewers

  • jjw24
  • Jack251970

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNxU3bABsvkCiQBHbGlcSHFcLzpIACIAMXgAD0gAYSsAVUgAejDYfzR6L3wGAGtkAGoUZl58KR5KADN8CmZMBhJ+esgAIQp8AHdESi78fGKWimKeL2wieAxoyDlIbEG+WiYiAE4ADk30Wlp/REHkZm9xbki6ikbm1pIAGj8SL3gVeFfcWSfMeiYlSBBNCfeRIBzSFBYXCwdo9fqrYajcaTS4zOYdSCxQp9SAAGTQ2AwDBhFA0RgA0iR5PVEhC5gxpgCjvgvBI5kRIAgiLAUullog0KR0Bh6GkAJKQer+EgAL3ZPF6AkizEQGkgYow6jePm+OXaqNmWAYBMG9DQ9ngRE1NONGFCqQyiG48GK7XwWBpFEQoUK+UgtGCYXwkJI9Rt8HY/uoKjQg0BwQoEeQt0laDZTAwiAA3DlUH1Y5LafQlgw8uIMByaFUms5QSKSAkIe6o7gY3GmM6IQR7NwSFFsNxU+nm4hbZqK2qACLYRMV+wkZzE+OUJNPB38wXtfPILk8gSiE3taH66aGxgwkpReAZwtXBtIGhEt1YEhSFyQV2yb29V056F5gt4CqXopGLeReBIMAJGBeBaGoeVgV8a9m24ahYGQd02n9Gd5TmLVgUgX1aCeMgVFeOd6ljUI5jANhmCaeQghXaQ1S6NASiIXpCXodciMYQ8wMlfwmLtFtzX8IjkFwRMiFIfxBJpK5iUwUhkG3Z4AzaQSgMuEg2DtBD/RIAR8EJBh5TYZTNUQZhJSaPVby3ahiUoNU4HaejvXnN92nFIS+xlR5+JWKIlmPZ5638PgAAoAGVYGcbgADVRAIL0AEp0FwKtuHLStg2qNpjgUCgKAHUJYqSgBxIdkMzRgy1C+RxPY9oM1oLV3SedSaUbQSMzfPL0B8WqMyk4MrAAORq24WlCANZw5Ij2SeCCpAMucmFK8r+D4ekWFQ8QlXaIDN2TXpbLyYl5XC8UyQMABBA5OowAidJAvT2GQelGXaXr5XifxGiSapFPaDrRxqSh5BLJp/DELxwOcRAbphBV8DB1U8McoNOV+K4MHdMBvV+ZwCiKeD3WQX5IFe5h5VHGE2FVSAbAXLwwHENh52y9lkCs1Taf6MIZLkyo2A66hwejZ5JMgPp1FgUzQmE7B4EW9BuEua9KawI5WwoXBVTcNGDTmQBMAmQEnDYHdGirUgsWgBepvER9BEFkIlcndUzECeCHLjQWRDOWucBGceWECuEykWcUpCPwP10XC8OOK4kVcddEhB1uiUjm4KnJBINVkkUdogRBSAyASp99KNyEJBZUDnnotk50JAV6naWNPYYSBG9gyA2GhRQ/cLBJ5SYKoPl1qvSqaMeabGllYPgucA2140aD+MvIW7c1EAS+TOReXs+AZWMx6Oc5cKwd6ofoEm1ErmmWjmVs5neEF7osFIWDrq2jhxiuAMO5HgrtnhMS8gSEeFB/YbB2JsJ4JNt45GoLmZAthx50mOIGMwABmAA7AAFm2MKegBdjjwCVKCXB7QCGEPwQARkhA5LEwt8RmRJM8Sh6gGJuRhM1LwiBgyE1QeJPS2hxwcjBIGPoJJ2giO5pVGaaY6rU38ELUIzpLz0AHN1BAS5jx1kfPQfeBwjjfU6OFLBqB2JtFynQe6pc7SJgEHgeyAApdixQABMABWJhmxCEAAZ0AlEJn0SItBSBmLRhmaS1C8DwGbDTOYHUt5RGMfLUyXh6D+DZCQHENi7DyLIDwfAVDjpqm8SUAJQTQk8KaKg3AfRgxvywNUY6Kp+ACFWM3NA9QaB8HCv8doQN2JpQbk3AGaiMwAAlT6UDXN7emjhERjDjriROSg4HkMxOrUM+AEgbORNs/IlBHKIA4EYJhaoHrXFmvcZ4nFpAo2bOpMRURBnDIchMsQTRuqKwTvkG6QEIQpkosOeq9I8goznCjK08AbSYAiPIKeqFj7dnCk0S0n8kJVGVOwXWapYj2QbGgQlQVQ4cl8UwshUL1FBlGJAAAbEw/BPTcWGkQjDPAkAmHsqYT0n5lz/lpRzDSyAxDiEsMZWNSEpYFymiEiwAV/jWWsp6d2fx2xmEqnur4gR7RAZHJBr0MG8sCxiLstxEq8M0XoCGZc8K4rAU9lEMinWOoHIuyJOIZsprganK2Ts1yVUSC4CsBaj4JANTwCsGhZ4uAZzkDNHfIkcNUq7SrlUL4PA0JPGNCseUlEPgziPMGQomS2GHOBujMG91al+MCcEsJ1Q2RKHoEoD+wj0AmX5cedWBygbHIbbGhOW8UlYBpqM90j5QgYz1MO0GsasaakcsgpWCs5zQnQa60MkymhRDGVa5ANqtoOrdpi+FlY0ZKEouccdVwlCItegGjAxrn36iTf4FNFA03+kHjauaRi0aTOwARe83p5SrquIdWA90NTPCdFTIK6x8BbF2OE4okTomxL1HGWR0gSIJEDnhXdCVQjhTPk8p8CdirqWLSqpYiAFa4Guru4Mt7Iic1yAuLtds3kIWQJixdnQlDuNkqjdo64nQum7hnQOGBAPdgKZQajBVnCIReDkPIHUJysxIEQMmMmR1mu/UByGb5dQYaw3sC+iYaQnvdC7d5WAoZ8G3eZ+tr7LTvq7Pew9T64PtGiiWucAADScwWvC4AALxMMi/3SgDhkDRdiwlgA2qF2mlKSAAF1IuZRpl8uJ6COphkoJGINY7cstFeqQOuQ9I1K1oPzTAwUiN0JZu5eQ/g+i9BoA5XLEMmA2YTrMPu5jDhvIwfs5TgGXaG24TQGDYcjmaMGCKeUA44JrfupNYMy3jzn3/l9HNzJpgfrUtV54W1BPJ1NiBWCUQxsrHc/dfQxhwBQDIPQJdMDCCkHIFQVBU865cF4L0kQYgi4yHRWXKgqh1BaB0N9kwUB3DfWpjOjxxAyDKHB+du0XAqA4gcE4d8sNdkqDUJobQugwCGB+6YAwVhTxzEQJkdhfQNCcK9q5DnaJP1wgGEMEYmyJiZGSKs+A6zJdnLDaSBg1yDDRA1wYX+D0xQE9B1LR+QDawYgFtIE2bUa6C38K3doDZN7qBxtBx8WEXIlFx/QBsDiP14xFORDkzhTIZ38LMb0K45zpMkLBSDvgY5S8mJjOyfB6KajSrfSAkXzm0BDRMRAkWnj+EDuZcPoR2M8gPuyJSiFcbB4fC6tG7BDn8kuft80HUHUMUT0Pd0fDFpqmQxnnZ2fSixAuvESIefICeNigAeUmgWr0CEg8jG0b0M++aGzSUmdO9AmjBtoC1leGdItZBgC3kuJURRJh7p9Jh9LniREYAAKIJC99O5LlBehei/ZFx/GBHBE5IDJTyQl1jgmGS2Hja2eBpHIBkEVzjn2A6m9yWDt1eHMkdRrloD934AwDdnCki2iBnC8GiGSy+F7H2TfGRWDko31CODICwiXXwLphIGIP2XwMIJYOqDXyTEWE227mejDjgJz3z2zi8HYkMjWhSRWCMkfTi34jjGvwQNCkEPjlL2VjCFkDINd1KC/Q/3skKGkwonsnpioSi2iFIOYOS04I024MiXsEGReHkAwMiHPQgSn1nyriJTtGNgMH70z1iDmSphHxYEnAEAn3CggMUCFhxE9C8i0OTHsnCidzoLdGsXvWjHDjjEtX3FuBoKKD7AMyID7xVhENaikme1fEkI6xGm7Ei38OhUWS8DPn5x2TqPUSCOYBCOS1L3T1aIWSWVJGf1EDwBIAAHVFZoA9JuBOiv15VmwpV9DrxIQUYARwolBIgTNhtUA3V5IuAHdoJXh9sIRY9kRIA0gbBcQZBeD+BexpF0Bnh8gwB3Q3ZYoABFXEdQNqd0cgOHd0P8NGV43EZcd8EKamdPKqXEafLoSw6gYZLAbo8KJWLyDfKgMQKIKUNVW6c4pYmgP0QHdPXEMUckR/aE7KSgDAP4v6AIrAMUScfZU6IUFvHfcZSNRMV8b5DOAUZubo80MGJgxYWMKIZsBEypUIGmGkqcAcVAqWUaFCNCDRI8FE10MxYMNMfAQeeSbiVFOwqQReDOY4uOeU92N9LJNGWYzpJNexJoAoxGL9DMb473DwVfSgN2AOSpdk+gdQUTEYXwBkBcY+UVPgEKNUZ/V/ZsZmAUK3F4A3XGM0quBITfH4mdTRPbaMlMRzdQWQe6NIP/FVSLCLDkAAWWvF6BESGQ0EnGjA0FigCE+BICzGSy2L0gflVVsnCkbXMEsAejiyJ2nXGgciUEc11gwk6Dt2aUFL4E8CVEWIb3EDNwMCgCO3PBUi7GDHCjfWoArWHLjILkNnHPASnL7hnO4L6DuydmLkgC7N8Hokqx1huyZL3jJIIm7AiI9KpTriHP2VoFEXwFFO7KT1a0iPXP/SbHPlEOKnfgaFKLJA12iCMAgDACMGF0NG51535wJEF1JCQrmA0DFwRGUJ5zrWOSH0z1clVxuRgq107N1xBwAMNyp3kCXVN0QHNyXIrAhGtyhgchhMmM0xeVrz4EtWTx71LQSLRlqySH1JzzEgyKCgL1EKLxkRL2BXNAh0jBQKkVRnQSEtT3D2pgHkEwDErX8hElCBTLWy61gwaCaAaywmI16zRkksmHmMw0WPREiwjVwCH0QHaMTWhGSzktKLuIJMmheLABko93jLIHcxaxHgBywEi2rNrIrNbDZguQoC6OUpfDI1QId0i3kVjWin01clStoGinSnSmS0KHwBzgKmqu8GlJTmUOQFeG9BmKpJBQKJisgIH3yF6MCIuk6JQGQG2KyWO3VliIvHj1EttwnlD3oyXTbPapbxkpxlnRhCwBCh6KpIaKaMGIYGGLGOhAmKqGmPVEzFexG20x8F00KmEznCXUcrHjOMBP2NXm9xpjOIlBvQQk0Rt2LCuOqrIDM2WoFIUBU1SmnS/QBKBOpHsjNJx1YoI3RNsnzPxMJOJNxncohKhPlmUoLVJIAyHmcgQGoM5BFJ+AznCiYnfFsMGEiDEFgOhHT1gmSxpkixb2SyYGmGYEzDauhRxjplvgZGwFfQcjqnVFpP3n0uwk3ilnsvaAWL7hTE5NLXavpJwQtEJS9San5EMgzH2tKkjAyQ/WN2xUVO1KiAQ3dzBsQOnV5W1LM3pxaEHBb1tK+IhubEdMBooBdKQEoSiG6LzNvVZspviXdsTIqRZCGsYEiGcG+WdUDMGDVBYvcsjWjQxljXjV8tgHAIAo9P5gOkiBoDdgG0TFJKhC0y9CPDRki1C3XXgGS0tXfhVKA0TONyXUGETOts/CvhKIUqEkFAnXIkgnCvsESTnFAxJsKPVGojfIu3NFaUIFD0HG/CliIHkG7GgLiXBky0s07o/S4BiNCCWGrTXn90swQxbIcgyxkNwGS0/FYUiyyw1BJh8AACoisc0n6I19cvBP797eynhjwPREJ5RU4r8W7ctA6Ytb7EtIsiiUMm5AtcjLVL6xpXsNY8CxREA2ZRDxApBG7RCiAnhCY7hXg5Q5xL7BhMVqAF59k/1U0zMVARFrsf0maajC8WJEAayPiGzOgHcncWYQzs5vcnDcJqYDhRrJs55P9raGHCbOseL5Bvx5Rmwy1ph/Av0JDfZNZqh2IeRUbh7IQlAJ45waYHA+lx6ORJ7xGW4H5bSVgCBbIEqXhUp39Irzqwy87sD09EqPjkq0ASrKAGzhqmzm4G8JJMyjBp83xEIgH4lLc6RgJOLFRHGYDrb0z80lgOK24ORBLu8dLiGeF5LJ5HG1UPiwdt8aZvq5xuiUDrwHcqruBrbvxsAxAK0KFkYVpKgO1Vb+bqbmoOS1E5xFaKad5MwV59t5RdCvN8Z2R7oOyLy/yPzsUgtBy7yl1RzdyAcJzsADyq4DJZzmL5yjM/r9zUCx74JDyPHoqXyuBIsxRf9/9kdIhgBoA9BIBYpXGxA3m9Bop/xkA/GaAAmgm+Aiq9lYgzJgAgWSAQWBNlkYAPnqhYcP0KrH7xLiLlcNBVdIsTmCzFBtb6AXyLQrQNzkyvZlzix+UdJPDWxvdTdaB7nqgaBUT6B3jvRgAh8PmPKvKfK0JooVHKH5LpBs60X0QL5ipajCKTllCSKKA2JBgGz4qMXZWsWcW8WCXnMiXvGgKK1lgKW2KqXZ7dJ3z6XEnGX09eBJBpTpa/CqTvKBqBABWrH/QBBs6nh2XcBOXlCPnHqxWjQwL0sVXQDig5WFWSAlWejpXMW0rsXc8NWbyohiXdXyWGXFhqW567RZ4GWmXExoJhsUFFjBWOQPL06wYs7+Xi3v0gDdlRXI2JWg3o3VW0rI2pXR0ZWQ25W43cWKL5y2csLMwedsQ0KuEhdOdRdehxd5X8LUKBcXJMLx2cLJ28KQ243kXyLNdtdqLCcwcohKdgETdEnjmwFkXs1LUUxwpcKQC48pgRcz1lhuBDjlSH3OILkHIizSxKkMZNAAmqyeHhtUIShNwr63xorNgNAQkNB8FcZwPIPCF7lnoTadR4npRaYikC0gOhQgZqsnxbtNF8gu0uBYpEh8QTIv8xQWhSB4pEoUsF9mx8ENA7kmEQknhYpig3hqOKBBxQPt8GOmEgkIOxnPmJBCi2O3haPoqGPIOGPIBFyfzuFT2xB0YuCSMwhnBSAjYnh7IElIwPiekTzfrNWIxaAFnKKlnhkVnVy1nRDKmqYMQtnUF7JJyLmjy5yjtyBoLNc+2EL2dx2ULh252SQNAB2l34Rr3kRMgdrKAedtr+i42N3YKt29daL7Ajd3xGKj2WLiXtGVg3YmD6AeraA+rMx2jBr1J/B8vMa9qDrxjJjBqaYRrn2OwN7TSlr0jQb94whJiax3wwYi1GpjGGwmwsAnRPUbQ1Pjo1JFZ0QJEHjBsPjPm3iFu7SPaK7riuZ4BAogTVw4yhjhtzRenBMt9mwhS0Ycoev5BJwuh8bYAhOoq9XfTMBbYFqzvJicZujUmkSX8RHt8xGzGM59DDRCijAHlyAcQrX82PJvGHnEBYoROuiCwCOpGe1s1kVKhgPGSC9aCvDhRPnqpFh0VJqzM0hoBYgwAyEByy5H4DZxN+yZYUwVFIAAANAswE1sIgbw0H9DiH6U4lyLFxQaOHogaAfAKwCsBH76VxRQNpqRgaDTPHmqdWsSXGc0VlYhBINX6waaHGVGsTtATjwcGmIXqs9j80V4AQKgVklmMUG/fIMo9oFRZBY0ZwlAXimkUUtldXzX20aCXuyKb6XAITzk5B/gPATwUU0EqaGaWNIFJmmZn3TAxfcmAw4H0BGu2KNMEgLodQZ2gJ3O2K+9tN5GzWy4eQeboacnAn8y7sCGCBwsL0UILQ3CVI6WVsaOxnped0QaXCaiToERHMeTygBWQYJ4RpnZe39HoUVGijzcA34QlGQKAPlXomieM4WyJdNXjX4hHgWkYRIPzPpfxHrXmqboh9AkWQjFSxSGmABAU4bx2w+R+qc0GOWOjNDJM+1Lhge2HNdRvVmmfQ5ADMwwhYAWgXgWaFeEo4gUwaPHNRtoA0YsQWKOXRANemx6hAYeQvPPl1UiLlcwmTiaJrEx8DxMLcy5SXtJGl5YRGeDgLWM0lxpM05edHNblH1H7JNm4SvW6iYQ5AfdpAm3JPonjmhCwyGm3XWCRAwA1wtIi3d4sNiu4J8sC3RdJPeArBCd6ImiT7qEHj5/d/cQeaQKZAoBYQGsm4OuKZ21zLM7yqzHeus17L2cyMY5HZuc2nKHMkwcFSAAWW8YVd8s6aR+P5jJbtAzKdAJlnswuaFs+4trFova1K5Osq2ZpZIPo1hbqxkELrWgG60LQXlEywAYth8yIgPREyaLQAEmElrAIUWzpaLFpa1XGgIdVgDHUpiEQl1lEJiGTg4hY9DWIkPdYpCP0aQqxh82O4YAKqeLbxkjwta10820pIIYsG9LqhYeInaKHIBoBZYCsk/WFtGB6FQAXBBffobm2tYFsihfcF/n/AwCC8ROIvMXkQCmGyAZhcwxACJwCbxCNYyscPnW16EF8U2Pgx9gbn8H7MRh0tDPlIGz64Bc+0YE4WcPmFXDGhqjMPngDraQA8htdAoX3BGE7CvhWfHPvvgCYAiSAswoEdGGuGgjcAdwtCD0N7bwVEKfnIdv0BHYYVgui7K9tOxDaZACyUieLurk3ZUVkuu7Oigewy4kDEB47AAOSS98I5DWeExnNYq8PYXsXoITFy7yA+Ij1VhCnB8ScRA8eiW9HcUGDO0lYmibsGtEjD60ZwFXW3u1mEKF4zMSA+wL3G9gSjmqOyCyHnT7wp5tQQg73CghOhSRFSAdPGo3DwYTp6gRDdPAAH0kA8acQDBECi0ASCBUF7ACBCgCV8i4DbCBrBpR81xaxoFyEBnbzvhUAj3Y+NkWPQdUwGTVO7t91yiWCFo8oKVHHWCjchRSAPTDLEhEEOBMGpscdsNHzBfh7iLkXHJCH5Gbc92dLYuCnReIJhZA+fSArYUSSyRUsdxM+CkgyT8k04io2WDsirgTZF6Q8OYMMT7JAwjgPIcSqMJDZTcR4/KS1PmA4xT0ngRASDBQEExLAzel+fZOaA9HwQrgkWf0TgxeBWjxekoIhn3n4Z2jgxs8VAPtCpQ0BR+xRf9E/0nTBiog0QPCEGPIbsgFg4ZTcGqAHHQwUM5wZAJtQghQQYIT7GUuaWhDW1FIvyNQoFF6DEwtoHkYmhCDKRH5Bgi4HkMMlsiOlpA7ALTpeLDwchr4cWBHPYAon3Qx8iiL8FWCtQniJx5YmvKHmPhnxFgy7ZvDLDbypQO80UG1NJLBiZRnyBTeyBJFfBalHKYACSKmTXRqgRiVEvgGyRx7RRZclLJ4GzHy5PAYsRdOgGuEai0ASsmiIidVmfZPF+sC4JTpvTXSXNDY8obotEC8oLB7IcQe1tEDVC4Nx+IsS0HJB1qWomK95STIHnEHcl+IT4HwLPAICugRuOgrCDXyjI+S5YSwaDp3XdAGispbQHwLBkTBaS3xfoVsIgHjho8weS4i7EUANr3QwEIwiSIuOJZMYu+GmKRj3CJBPBUa4ULnK2How18/abpESfOysSQJggoeDwQuL9AyjxpxIcUb7Ddh1NCxojOZoZjATRYFpirLAfQFsLAgRENUl4NbVdInB+yxkdKW1HuA5TvceU8pCIhnBtA1Q2ZEKH8BrjkBfA5jSRNwHVEj0BS+SUMFuOXCBhT0OAv6vdBibKBCBDkFKe/F6CaRu4oo3aT7BWD+xXpZkJyQthXAEs+4A0v0IrVoE8h74zcHUYbSJCI4peXgEiPPFma+5umNMKMZKBjE+I4xt8LsTKBJRGBFmXZCzqYKs7mCbOH5TZtYO2Y5pnO9g8II4JOZPRBMPPTYVc0LAvABh2sxRFsOcFSJIA/ozMLNLaD1tA26eOkXMHgYnMrJhrK6SS3fQVp1hkPS5uIGCFqk2Wg+JqmKHqC/4yIdAMqslihGGz3YvcGALGEmCZ4vKGoNiHOO4gPR8ZYc1hA21tlSIHZUAJ2QRmTZeDgKbwi5p62ABswHAcWD5ihJcDRRq5jEQcbkPyH7NS55c84FXMHG1zBxMNNFtyUkZmh8Z5onRtTOT6uVzqAIcVjbMix2yMAOclIMKILmksi5zNO0I+V8AfDfZRmEPMMiHwCTnWGsBDI3MiwQUAMBEdeYPDZhbyhgskigAE3qFpjZAe82DHiOtmXws59sskFAE1mW0hhOs72VdI9nDDjZ0tafBgB3mxo85ocwQCi3nCRQngAk2KEJL0iP51ouAB6BQA55Vx/WMdV+VPOzkfy55lLF2U8OLmFDdZ0tWKfkEvJeUphYwp6YAVjQmTRJXoSAPFhFjBBD5Ks2EcbJ2kxzWpRmTIT4C8qpze4NCqOnQoEmMLlpLCthSQCwWZzcF78hNoS3/lNyLm0teoahkGDpyJ5OC6eWGPulgyM4dCl6QOnoy2hapnooUqMB+mFS+xBI/tsSNnboV52FIkXKFynZD5MgSUCMAMBlxlNmAVIr5rzAnAJBKUXgBLmZx1wsjoy+7dup0CYosVTcdjHUiqIrztAJi8ZLoNWkmCuNmsxfcKEzweis9CIQcNQqXhm6fNWwJQRNKDIESoAMlnlbJVZkDiyBvkkAc8RdUQlYdQwOHcQWFHvSexKUixZkH9PaAP1og3o/oKIVHYUBfRZsDAL6It5hcKAjlX0R0qUAuDjgm4EJPgmik39mSrcAiIMi7gM0HIbIBwARB06iRD4/QDNJjP8UyTllPMIaDui/I4h3E80fABCBtTAhfkmAGGBHgrBjxzJoQJwqljYlg0yBvgSZTiEVpGDOyJgywWYKMgWC7OCsnco512b7NXOxzdziQEZGwVvORIkXP51JGBcx2biqkZ4u8VFJucQSvKIgBcRQqNAoS5gOEoJWRLt2+uVBLEvS7xLMuafdoGoB2yGERkNdbBrLlEBKlLCTpQ2AxRb4xBg5x0TEFFLnlSrhgSQIUYQuL4uNglHPDQEqsiDFdc8VXV6MdGNXwMb+qAa8trQn6MkdO8ZG7k3noAXsHKgKopjkzMzHyJYbwYbLqoZUyrva+aGmMKs6oKSEYG9YMD/jNVGr7WDs8WcYKllIqZZKKuWRsxHKKzMVdgw8g4Lc7ug7FXnQkb5xJUki+c5KhdpSuvnUqfFdKyNAyqZW9AvALKsJQyIopJcaKrI1LvRUPacjBVvjOtXzAbUshOaNs9SL4OfY5Nq67QBKoguYAaA4A+meZtAFjkmqdIzSe5CNB2mDypCzvPtDUWnkaAKFtAKhU1XTl74D8HpMedOuXWtTD1hIM9XdgCoZTgU4axvohD7IKLP0R6k9XuOEVEgw59yNDuD1lX5po1Ici1fe36EGJrwBjF4dXzDqG1UMIqjkIMD1VANRYE4z9YeqanHrBFTVP9QwDDlCcqASACEFYTlWI12gYiL1FOiphfpaZKYAjoZA6n7VvQaqJZVO3vYowtaWrAnsUzEJmMiaf+AiH/h8Cn5JqPjcKB1I42rBeRynawtRLxqvrzwUqyTfem1CYY0JshaKAlW3T1DgQmGMqiwtYXSRggxWDdb4BBUyCxJSmEDcozrUTiFCY4xKcWGUKbT2sURfiCNCw3fq8Nv6tOWi1fUHTFYahJ0GgD6C3FzQHUlqcUHhXmceydnZFQOTTWWD0VNg5WTCIOZqy5yUAdOmvmah9ySF2wsYYasAJxqM5k8+lYOvnSNrW2A7UlWWucVBcQuVK/CjSt8VVagVQ6ptayqbXqt7FPneraWrJEuKWtVa/CskH8Wy4Lo8uAJc212StqmRF5LlSl15XyrKNx7BJsuQQKgkOpQdeUAgtDxzqxQ0+esqmIhq1BkV/PKbXLgVyds1WJqxtLJ2DCD8zsAIPQi5T7g2qUUd5csWeTi2SyEt9UJLaIBS1oqM1GKvcpwqy1ag81HnDlUSuLXIVht5a1xYaHcUrs48mQfEBWEgyqRMgzgZtWyoiXtqd2MStLutoSWPQgNF5ZHIsVPoCjHRLreEmjAfqRYYV0yjCnMvHaLLr5qy+ZebJFnsgJeihZ9gel+m6DwY14E2i4C/T7FAwL3VAFW1QDRBAANGCAByMEACMYIACkwSAOrsAAiYIACowQAOJg2uwAGRggASjBAAMGCQBtdmu1XYAEkwPXfrvV2AAxMEN2AAOMC10aAfdCwfKkYioCZg8GK5GIIGPtEUMimukUGvmFRy+61QcnU7Btvlink32yK9sgmoRVJrEtKa5LdpnTXbl0tTnTLTiqcFfyzQNOkZZLpBFzh2ALge5hzqa2UBudIuXncsv5086YJ4e4XY/SG1OKZlaO7Cq1ppE47zxZ0AnaSF609tC1DiktX3vJFjbllnikfXjukCZBVcRO9lW2uZEdryd3ajkWxWOZc9YVFMchj2I1iV6sILOsZVSBiAN6Zlzew0K3qnbt6W9ne4MfBP2ThRkggUIxBvlD2ahzIf4toE8EACwgCQFdBCAAOJZAALdzrfdkGvubjHF22LW6MuzMlaqtgutXgroEutnFQGgkuliibdFgBLFCs72b+xndfwT3cIUp3YJ3AzCsYYQ+ArTdprvhT1KAAdiK7PS9NRX1Q0tSsovditzXHNP5iBqLehyrYX7r98gdndiE53zsH9cwJ/asBf2P6KDPAj8d0Xl3Trog8aQAwKOAOQAwDEBqA5UlgM+6NALBJ7MyQl1YRjadtd8JFl70BdG9Fa9HUPqx3L6x96+yfZ50JVFrnDZK1wwPonaL78KXh/HXBA30k7t9ZOnlRTp7UH6QeNOistZB5AM6HR2+Ktlfo/A37ZDUy1w4oYWUybKAqhpQ+oYj0h1t6/cYEIGF0O/iWq1WPZCgpSw4FLQrVeA+pH6FIG0YUh1Aw4fQPY5q9S0BTLgYgjbZ645oQg/YGINCzSa8yuTRUdniQyBS8el7YntoPBh6DCKRgzmhYPAVk9v1C5Jwaz3A6c9oOvPaloh2F6sVLnYQ6XrEMV7tBoy4Y1lrr3p479XOgXSUZWXKEijgu2CRoaIB1bHFLh/vQvo8XhGVIK+7nFEcn0I6AjoJoI+CcpHjbh90JsfRwd60xHlt0S+I3vv5W9qj9EE0/dT3P3PGq9OR8ZZ8YUPfG+dfxgXRUY/1rV2gEaO4FCD/31GuxKMSgISCKb05hs+YXcp+k6OI9EDyKvo/YfdC1g6lmB+MaMZ8njH56LWLZUKBuURa5jHIeZZ2MBOiyP0ax/gBsaPa4xtjKG3Y/ZH2N6sDOlEjg+LMiVcGzjPBsHXweuMCHbjqs2HSIYvKPGOpkhik1hDZ00mSQ/xn42UYWVMnNDwKbQ+ni5OAmeTZURQYsHUCCnnAj4Cw1YahA10sTYS/yv6al1t0+VDkDwycXmV+G4KM+5HXPtG2omwj6J3HWPrIDRGOVpO7lXuwSP77VIyRjqRkb1NZHmdwKcKNSbkOFG6Tbehkx3oaNAmFgLJmo9MHaCxmu9iZqPXGBj2aBRTEjQTMit/xEAWq6RjE90tsP5m0DneS9tfO6BNVb26OjA68YAkrz6wz7Q7t3CbwUAwAlEfutMYjxTpKM+6RseQcl5i1JzvZlCAlCTp2nE1QOvsqsQuO2cXTBet09mph1HMjAeKhExWa5wo7gjEJzHRFwiOr7pAYAWVOB2xPNnYjrZtkXEo20g8fT6HWKKhDSMknMjI4fs+w1Z036Jlw5+/aOef3jnX9gF5k6HXaDRmuT+hoQSKHwCrQ4aqpMyaNyoDpnZTrx7Aw4R4T4G7i0x9UyQZwjzGeRfI3U8sZAvnlqDlyTY3GQfAMHFoTBxoW0wOPWmiatpjPfFpguQXrOlx8HXBazXQ6S9Gs6iziD6PFt69HFr4zztDM8W1DfFj8W5UCONaUTla2s54f3N4XEABFgTvCYG3ErKzYJ+fTWchN1nR9+OliMRa324md9+J9kYSaSPU6OptFzAEgHSMn7GLI3ZiwxNYvyB2LBRzi0FfpMht/jEZogNOYEuzm6jeh8Pb8HEsVIBKUlquDJbQDpmEDm5lNZKel0DH5LVbRS2MZUtTG3kwHdS5qZGzaWdTw173CsaTrPbDTNB403QdmpDRi2Fly02waON2WJZDppy7LJcuwWHOUO4vfca8tHcnjh5145+GwlzmC06sfy21cCst7grXVxk2FeBOQiYzQ1v8WJYkvjXG40ls+NNd90gnZ9GV6szFeytxX6zeV1UClen2DakTUVzK3jewtxxsd8V7nFKCbOFWolxVtswScouPRvLmICrkuB7OzxsjA55q7foCu0mOrY5qGxOaF0Vg+r1RoS2HpggCgP0TwKQGrGuoyh8aEYFeaSDj3XmVrCp5SyxJx4bXVTRB25Tte1NLH9TJ117UnouumWdj5lvY6ZtYNbh2DfY+y4Dscs9GXrnt/g+5c+vZavTZeu4h1N8tWNQbUSEc6Le4vi3eLkt2GxFfJsjbmtWV6m9Llwv02J9OZ1C2TexvInKb7htEwTdyur6YQjNpbczbiOs3Sr7N4k4sgt7odebTOjWFSbYtBmm9XFlQyFfKMw3pbZy2o/OcAB7oIAAXQQAFeggAddBAAO6BPAh7gAA9Ah7kAQACugA9ke4AH3QGa9FAXPv6lzsdFc9oDXOWHMoXR8U/NbzP9HpTvXaat0ELslnF2Vto0yQJNOXWzLfMR22VGduHGbTbtx66ceeuprXrW5d67YI8tfXRDP1kO6fara17ZA4d+Q8GY7ulGu74ZmG1jfSt53cbBd2KzhbpuZBS7JN/w2hcHZVnk7VN8LjTfTuZBUcBV8uyts7VrbEjnZjm2A/Q428YInWRu32ebvApAzwtuB1Hc7sx3QrcdqowNfnPxo5QwY0WXKHzU/By6uyGgsZnwByWdbWBvW1jwNuTGVTEZE2xqdINammxFtqg+sbOsP2bL/2sC5nogte2/7Pt1037aEMB2HjgmUO4tBgeR2IbnVuPN1eQc97E7qOrC6Q7TvYPKHWd1K0jvQtEOKVGD/G1g8Jur6hA01qh4l1Iurb2zZVhh8Se8R0WWJDFoCw1c4csXpDHxnh+3b4cIOBH3doR7NacQz1kA0ZlXYAFwlQAPjmgANGVAAtHKABzBkACaDIACAGQAOoMgABwZAAkQyAB5BkABSDIAFD9QAIYMwzwAAYMgAVQZAAIgzpnoNAezAMgLlq4xN7cEwzBzmVROQHcUxlgOwCUdDHldpA/7FI0/PSkDu+jwC3zbpZSFCDBp62ylNMfHHzHDlyzk6f/tWDIdQD/256ccdtLwHf1qttw7Bsi33HYtzx9DcqdyD64mTzANk83xB7/x8VSK0ncieD7r7ZD7B/E430OzQnGLvxynYCfFBabsT7nMUEUeJPOVeJquxRap3EnyQx6Nh3VdyevG+jLdlq23dmXwPfj5TpB3Hb91imu0RRWp/3ejrRBAAELOAAGOsAAqa5AEACIk4AFDxwABNNgAE6a1QgAAvHAAO0OAAZzsgCyuNXgAEg7AAPxOAAOQcAApY8s8jgwaRYaz4PX2SNtaPjGt5TStRl/Nnhbzcdu8a1LMx8zuwq555/fYP0f3bLX9+0z/ase56bHblj6/Y8BffXgXEhl1n0fBcR32rUL6OzC4lu6nu9ORll3kBAGbWhQMrhV8q/VdavIAerw18a/NfWuZr1h9PMS8wukvqRRdmE5kGpcEvEGLxqU69Fl3Z20r4TnG8Q6iep3yX5DjAAIDLtJOirld8i4WaZc06ezZ++UFy4FuFPWrmb8G4/shu5vY7+bqW5/rRiy3JzqwBFutDaMc9QgKCma0fbmsOQjsFAE8rMDYfxW+3Ve89pfeLPwFSzyj+UzgcVPrXNHW12Y7o8vPogDH7oEN8Y7De231sZph2xaadvWXXbJxyxyDt4MAPM1Cbu4w4+Tfl7fTab0+wDejOoQQbRTiF7w+zf8PD3gj497DahFbPSNUUJW2QBvdeR73mNnx7nYpvoPsXmD3F5S8yAzuCXw7sJ4Q7HdYvQj0TkT8Xe5w4E53dLlm0u8p0CriT7Dpi/k6avbveXIZjx8iC8fCuqnYutGNOBPElLYnX7rCE9rATRnFdcp2xqx8vcYASI60dpXQG8Amdtbhl8+MabecPWo3WH84zh9+c3GELnl0Bym58sQOXWGb2ByU7o9lOGPFTpjxlSZrRn8CctxoxVw8/lJYkPn9M5G3CiWeebn7hawWfkA6bW30Vid2S4peKexPPWnM+lDLOI66v+doT/J8CeifLgKnlsyk7Zsrvuz7L9d3OE3cFPcjPL4p3y9KcCu0vQrpj73ZsQrxD4XXR1cJficGH4nq0EsjKEACYgIwECjFA0AgAcEAH3orqRjRj2uiEbPfR+z7fwUuqO8D6jgg6W+0caWGxJ4cg3c8Ov6W4PRl861safv22X7qHt++h/uuRvwLnt7D86dw9/OMtibpC0R+DupvyTf1xL24/3dGe44JnjL3TL7tA2cvABnb0IIYB7f0YiAI7yd/AMXeSvsomul18E9yfJ3TXrtwN8n22ejzS1yT6z/Hc9eOf5D3KGFUzvE6SLC7si12urujeG743skxu9PvcuhbNH5L3j+hfGfYXK3094JclfCXhrSNsa0OHsiCgLxHB9c6LqsdWBmkMwNaWFh6BoA5QXgTKPd+Lu8+cYrq2EDi4mBQfP015pjYdYjEQgXXwHK50NCc1evoPOlxc5oP7nxxwogb4MMG7vvwfBYiHq6+aeYNoerTGHj5x7a+dQXwvvt/Dx6bR8xelfk3vM646zea+c32vvN3H5Qeju0HQv9n419F+4BxfEnol747bckOO3MT5r2L9yiDfkntD1JzXc5u2+bY547J9p7yfBSt3M3tX7u8hf1/6Pjfo93H9W9nuDfDyPCCJdFl7IdypvvgOb7JjFxtbpzlR8B/1sTGPvxtmY6bcg/m3/vhj068D4fuZ/n7QK1+1ZZ5+MPph7w+YXoj4Re8FsA6Eelfr9YoGfltR7r+tHpv6pe2/ox7N+fHqg4Ce7fhjqd+2DqP6aAeDuWY52mAZi5uGwvrgGieZUOP7S+w3nL6aeNOjYAfYbwFgCL+rxqr47uSXvN4pei3qgHpeu/mZ5WOVXmgZy6krk543myANECAAGCCAAvCCAAHCCAAYiCyBgAAwggANwgsgYADsIEoGKBgAPIgTwIAD8IIAB8IIABsIKoGAAwiCAAnCCAAgiCAAQiBKB+gQYGAALCDGBDgbIHWBgAKwgtrv7oMSgeus7V8wYKx4R6q0Dvb7O+9nsr+eSekF5GUbZLGggBRfs5ZxugDij4EeSbjAHiGcXqC4usUDrX57uShge68By3ugEJ2/HqQEhGOAUP4KeXblQGEBnXv371e5AeUF9ezXreq0uQ3pP4je9AWN7O83YlX6cSKviv5Dm6vlwHIBPAQT46+/ATOaxQhQNBBX4nJgjbkMAAIeAG+3mYa2QIBmgBnekBtf6WGAgRKan2A7jKaAepYq95KmhtmB5qmEHppZ6Of3iLIkoaft/4IeoPnbbIeEPjn5Q+QAZ/axB0st84JBeHv86o+6sqkE06fptj55GBnvy5hmAJugFaGkrmT4NMwYosFvAywYgCwGRhusFoAmwUz7NuQgQMY4wThrUHdeHfg0FTu2Ds0E5mHXoibFBJLoP5L6JIRL6b61DvS7qe9DnORae7Lvc66eDkAMGIBGvrkH4+EwIT78Bj7jd7/EpRswHWeHvo95eiv7r74ogt9n1IusdThIFDWFPqLLmQ1PvRCvA9EHYTGG6ITQCeBhiN4GOuGzt2ABB3TMuYhBSjuEGvO+fu7ZPWMbtBbyytjmX45q0Ac9rw6ffpSED+DXkSGc+Y+tJDUBFdjL50OHZiyGruivpy59B03lyGcBhnlr6jBTflva9WevjAAzg7HBt5CWbEIAD6gHgyAAjIBeAOYbIDuoXgAAA/uAF3BYA+4MUCRAVBFd4bmwoTYYoG+wbLqHBIzMcGgealhcE/e/vvtZ/iAPqsZ3BAXj/6PBSHsMY3WufndYfBBfnaEI+PzqX5/ByQRX7emTDhTikeIITIZghC3hCE9WmXnp4iOMZlmG5h+YYWF7IJYWWHlIlYdWHYUvHs254hnoXUGEhNIaJ7+hPPtiHn2UTB6EkBVId6GPhzXtgDFAYAGkAPQAYTQ676dAUSZhhXQSGIRhf1uwEbh3AVuE92OwSmppAxQCRqagH7g96n2P7mKo++wnn74Ae8oRrCKhMQIABgINIGAAaiDyB5EaoHkRmgVRG6BkAIACMIKYEOBgADIgkACoFKBlgYABcIHYGQAmgYADiIG4G8R5ge4H6h9rii6+BIeqaHb2ezlaix6lhkD6DhDwSZYjh11gAHv2kQZ8HJq3wY6Hxu84eX4AhS4bF7QRKBjj51+vIXGH8hYwYmE7hJPoGD4EZEZRHURKgbRFaB5EQxHMRbERxFcRvEU8CCRwkUoGiRHgdeGZm06oL6yeZQT+Fduf4QBFARL4XsGLWb4eSEEODWiUH+OPoeQ5sgwEYyGy+jLpp6c2NKrgBMEcYKwH82UYa3ZzesYQ37xhO/omF7++vkDbRAgAAiAKkB+CwAgAPt4OgLgCAAhXijWEgGrCEQAAE+bQsAIAC7eCc7Peutvf5qOj/qpafeL/jo6XBu1tcG6WfYcdZWhgXjaHf2oXrpH56iQYIYLhRkUHZpBnvpA6uI0DggExh4IYg6QhtkRgGt+WAZFF/ujQV27ZR1QRSGfhXofUHRRY+jKCwAp+J+gtBE/qBH5RRJpzZruPQZ76wRVUTdGCud0ds5JhM5kJaAAxtaAAFOr1OgANOazToAAw/4ADv0YACwXoACYCjNbIqsuHMDZOcUDSyEsbvpV5YRUoThFX2eEbKFuKLYSMYzRb3nNFh+5wa/7LR7/jcH6mU4dG4zhPwcj6HRhkTlrGRxHpj7K+a4VdG4+lkTVHWRCYYjF2R4UNl7RA6MVjG4xhMSTGhR19BFFkBD4VCaie/0YDG9ur4YO7vhpNiO7Sebfs9EyhvofjpmxrSDlFqeeUcu4FRy4Tk4TevQTBH9BlUYMHVRW/rVFoB9UcmFCW2sa044xgANNRxMaTEpq0AFQC200pr4DkxMBGFguxfQLTGYRf1thFFmMof77LWd/kpazRyph2G8x+8rc4Cxn/i84g+koGZBIcGZDmgJIjap+LCwWkULG7RxfuAFzhSQRLGB2nNsCFmRoIbDGbht0duHE+GsVHGxx8cfrF4EhsaUEvRxIabEAxrsQlF/WTYdbH4OLOJjgHMAOJ0BA4uUapSk4fgOFoexMMIji04KOAzjo4zOAYB7xEOOoD+i7WL6IFIPinQC+i1sKEDfYD8b9iQA/iAID4IBwHqi0A9QIQhAJviAwC6oaAKyiiAtAIQjrAxCLQAhITCLQCbAmwCQC7AJCOsCEInQBjj/xrKNsD1A+CPUB0opCbQD+IWCb4ibA+CAIAMAviIgltAhCAwC0AyCWglUJ6CYAkMAxCFAlM4hgHvFMIhCGgAqAxCKygCAhCNsAoJgSOHACA/iGgAkAviAICbADAJsCsoUCTQm+I+CP4jEIsCfgikILCAQkQAVcP4gYJISFAlwQzCIMg8JtAPSh4JTCAIDEIJAHgmwJhCL4iaoTCGgDEIlECEiJC/CX/HGJ2wP4ghIhCJsBOJaAEoltAmwLQCMJmwLAnGg9QA4ksJmwO4n+IpiZ4lYJUifghkIRiVAD+IJCcQg7KTCAwn1AxCKElAJhSYQiIJmwPuD4IaACEgCArKPUAhJrKKwmiA8ifSj+JASVABPxuAC/GIAb8RURFIn8f9j8JQAA -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Flow-Launcher/Flow.Launcher&utm_content=3815):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (1)

27-29: Consider logging index creation failures for better diagnostics.

While ignoring index creation failures when indexes already exist is reasonable, completely swallowing SqliteException without any logging could hide other issues like database corruption or permission problems.

-try { command.ExecuteNonQuery(); } catch (SqliteException) { /* ignore */ }
+try { command.ExecuteNonQuery(); } catch (SqliteException ex) { Main._context.API.LogInfo(ClassName, $"Index creation skipped (likely already exists): {ex.Message}"); }
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (1)

89-89: Consider extracting the hardcoded fallback message to a constant.

The hardcoded English message could be defined as a constant for better maintainability.

+private const string FallbackInitializingMessage = "Plugin is initializing, please try again in a few seconds";
+
 public List<Result> Query(Query query)
 {
     // ... existing code ...
-    var initializingSubTitle = "Plugin is initializing, please try again in a few seconds";
+    var initializingSubTitle = FallbackInitializingMessage;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0c733b and 5b3dd83.

📒 Files selected for processing (8)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (2 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs (3 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (5 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml (0 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs (7 hunks)
💤 Files with no reviewable changes (1)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml
🧰 Additional context used
🧠 Learnings (7)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj (5)
Learnt from: taooceros
PR: Flow-Launcher/Flow.Launcher#2616
File: Flow.Launcher/Flow.Launcher.csproj:7-7
Timestamp: 2024-10-08T15:52:58.573Z
Learning: In the Flow Launcher project, the version number in the `Flow.Launcher.csproj` file is dynamically updated during the CI/CD process.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: taooceros
PR: Flow-Launcher/Flow.Launcher#2442
File: Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj:4-4
Timestamp: 2024-08-31T20:42:08.482Z
Learning: In .NET 8, SourceLink is included by default, eliminating the need for the `Microsoft.SourceLink.GitHub` package.
Learnt from: taooceros
PR: Flow-Launcher/Flow.Launcher#2442
File: Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj:4-4
Timestamp: 2024-10-08T15:52:58.573Z
Learning: In .NET 8, SourceLink is included by default, eliminating the need for the `Microsoft.SourceLink.GitHub` package.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml (3)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Learnt from: onesounds
PR: Flow-Launcher/Flow.Launcher#0
File: :0-0
Timestamp: 2025-04-23T15:14:49.986Z
Learning: In WPF applications like Flow.Launcher, font styling should be applied using implicit styles instead of setting the FontFamily property on individual controls. Define implicit styles in a ResourceDictionary using <Style TargetType="{x:Type Button}"> format and merge it into App.xaml, which automatically applies the font to all instances of the control type while still allowing explicit overrides where needed.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs (4)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3672
File: Flow.Launcher/MainWindow.xaml.cs:244-247
Timestamp: 2025-06-08T14:12:21.348Z
Learning: In Flow.Launcher, App.NotifyIcon is created before MainWindow creation, so null checks for App.NotifyIcon are not necessary when accessing it from MainWindow code.
Learnt from: onesounds
PR: Flow-Launcher/Flow.Launcher#0
File: :0-0
Timestamp: 2025-04-23T15:14:49.986Z
Learning: In WPF applications like Flow.Launcher, font styling should be applied using implicit styles instead of setting the FontFamily property on individual controls. Define implicit styles in a ResourceDictionary using <Style TargetType="{x:Type Button}"> format and merge it into App.xaml, which automatically applies the font to all instances of the control type while still allowing explicit overrides where needed.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (5)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3664
File: Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:326-326
Timestamp: 2025-06-06T05:26:32.163Z
Learning: In the Firefox bookmark loader (FirefoxBookmarkLoader.cs), using hardcoded Windows line endings ("\r\n") for splitting profiles.ini content is acceptable and platform-agnostic line splitting is not required.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: jjw24
PR: Flow-Launcher/Flow.Launcher#3081
File: Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs:136-145
Timestamp: 2025-02-22T23:51:54.010Z
Learning: In Flow.Launcher JsonRPCV2 plugins, the `reload_data` method is optional for plugins to implement. When the method is not found (RemoteMethodNotFoundException), it should be silently caught without logging as this is an expected scenario.
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs (2)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3664
File: Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:326-326
Timestamp: 2025-06-06T05:26:32.163Z
Learning: In the Firefox bookmark loader (FirefoxBookmarkLoader.cs), using hardcoded Windows line endings ("\r\n") for splitting profiles.ini content is acceptable and platform-agnostic line splitting is not required.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (3)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3664
File: Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:326-326
Timestamp: 2025-06-06T05:26:32.163Z
Learning: In the Firefox bookmark loader (FirefoxBookmarkLoader.cs), using hardcoded Windows line endings ("\r\n") for splitting profiles.ini content is acceptable and platform-agnostic line splitting is not required.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3279
File: Flow.Launcher/Helper/WallpaperPathRetrieval.cs:44-46
Timestamp: 2025-02-28T07:47:24.148Z
Learning: In Flow.Launcher's WallpaperPathRetrieval class, using a `using` statement with MemoryStream when loading images with BitmapImage does not work properly, even when using BitmapCacheOption.OnLoad. The stream needs to remain open while the bitmap is in use.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (2)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
🪛 GitHub Check: Check Spelling
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs

[warning] 151-151:
Favicons is not a recognized word. (unrecognized-spelling)


[warning] 140-140:
Favicons is not a recognized word. (unrecognized-spelling)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: gitStream.cm
🔇 Additional comments (15)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml (1)

101-101: LGTM! Binding simplified correctly.

The binding change aligns with the new EnableFavicons property in the code-behind file, which properly encapsulates the setting update logic and triggers bookmark reloads.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj (1)

100-100: No changes needed: SixLabors.ImageSharp 3.1.10 is up-to-date and secure

Version 3.1.10 (released June 13, 2025) is the latest stable release and already includes fixes for all known advisories:

  • CVE-2024-32035 and CVE-2024-32036 (fixed in 3.1.4)
  • CVE-2024-27929 (fixed in 3.1.3)

File to verify remains unchanged:

  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj, line 100

No action required.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs (4)

1-10: Good cleanup of imports and class declaration.

Removing the unused System.Threading.Tasks import and explicitly inheriting from UserControl improves code clarity.


27-27: Consistent async pattern for bookmark reloads.

The change from Task.Run(() => Main.ReloadAllBookmarks()) to _ = Main.ReloadAllBookmarks() is appropriate since ReloadAllBookmarks() is already async. This eliminates unnecessary thread pool usage while maintaining fire-and-forget semantics.

Also applies to: 37-37, 47-48, 79-79, 88-88, 120-120


51-60: Well-implemented EnableFavicons property.

The property follows the established pattern, properly updating the setting, triggering bookmark reloads, and raising property change notifications for UI binding.


76-76: Cleaner dialog result check.

The simplified check if (window.ShowDialog() == true) is more idiomatic and readable.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (3)

79-90: Excellent error handling for JSON parsing.

Adding try-catch for JsonException prevents crashes from corrupt bookmark files and provides helpful error logging for debugging.


124-130: Robust bookmark enumeration with proper validation.

The explicit checks for both name and url properties before creating a bookmark prevent potential null reference issues and ensure data integrity.


140-160: Well-refactored favicon loading using centralized helper.

The delegation to FaviconHelper.ProcessFavicons improves code reusability and maintainability. The SQL query is properly formatted and the lambda expressions for data extraction are clear and concise.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs (3)

66-77: Clear and straightforward bookmark reading logic.

Replacing the custom LINQ extension with an explicit while loop improves readability and removes unnecessary complexity. The null handling for the title field is appropriate.


120-139: Consistent favicon loading implementation.

The refactoring to use FaviconHelper.ProcessFavicons maintains consistency with ChromiumBookmarkLoader and properly handles the Firefox database schema. The comment about PNG conversion is helpful.


206-273: Excellent INI parsing implementation with robust profile resolution.

The rewritten GetProfileIniPath method is significantly more robust:

  • Proper INI file parsing into a structured dictionary
  • Two-strategy approach for finding the default profile
  • Correct handling of relative vs absolute paths
  • Comprehensive error handling

This addresses the Firefox profile discovery issues mentioned in the PR objectives.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (1)

102-128: Excellent refactoring to handle multiple image formats safely!

The use of ImageSharp to load, resize, and convert images to PNG is a robust solution that prevents malformed images (especially SVGs) from crashing the UI. The error handling ensures that processing continues even if individual favicons fail.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (2)

169-190: Well-implemented debouncing mechanism!

The use of CancellationTokenSource with Interlocked.Exchange effectively prevents rapid file change events from triggering multiple reloads. The 3-second delay is reasonable for filesystem operations.


194-194: Fire-and-forget pattern is acceptable here due to existing error handling.

While the discard operator usage could hide exceptions, ReloadAllBookmarks already contains comprehensive error handling that logs any failures, making this pattern acceptable in this context.

@dcog989
Copy link
Contributor Author

dcog989 commented Jul 10, 2025

Note - still an issue with some SVG favicons not being picked up. I'll address that if / when this PR is accepted.

@Jack251970
Copy link
Member

Note - still an issue with some SVG favicons not being picked up. I'll address that if / when this PR is accepted.

Thank you for the contribution! I will review it in my spare time.

@Jack251970 Jack251970 requested a review from Copilot July 11, 2025 07:55
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Performance and stability improvements for the BrowserBookmark plugin, including asynchronous startup, debounced file watching, and guaranteed PNG favicons.

  • Load and refresh bookmarks asynchronously with a semaphore to prevent races and UI blocking
  • Pre-validate and convert all favicons to PNG using a new shared helper and ImageSharp
  • Refactor settings UI and browser loaders for real-time toggles, improved profile parsing, and removal of unsafe async void methods

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Views/SettingsControl.xaml.cs Extended from UserControl, removed Task.Run wrappers, added EnableFavicons property
Views/SettingsControl.xaml Updated IsChecked binding to use the new EnableFavicons property
Views/CustomBrowserSetting.xaml Removed an outdated guide message TextBlock
Main.cs Added async initialization, semaphore, debounced file watcher, and made ReloadAllBookmarks async
Helper/FaviconHelper.cs Introduced ProcessFavicons to index and convert all favicons to PNG
Flow.Launcher.Plugin.BrowserBookmark.csproj Added SixLabors.ImageSharp package
FirefoxBookmarkLoader.cs Refactored to use ProcessFavicons, improved profiles.ini parsing
ChromiumBookmarkLoader.cs Refactored favicon loading to use ProcessFavicons, added JSON parse error handling
Comments suppressed due to low confidence (5)

Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:200

  • [nitpick] GetProfileIniPath is quite long and handles multiple parsing strategies. To improve readability, consider extracting ini‐section parsing and default‐profile selection into smaller helper methods with comments.
    private static string GetProfileIniPath(string profileFolderPath)

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml:101

  • [nitpick] You removed the guide message TextBlock bound to flowlauncher_plugin_browserbookmark_guideMessage03. Ensure the corresponding translation key or resource is cleaned up to avoid orphaned entries.
                                FontSize="14"

Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs:135

  • [nitpick] Logging the entire JSON fragment can be hard to read. Consider logging just the element's type or name (e.g., subElement.GetProperty("type").GetString()) to make this error more actionable.
                Main._context.API.LogError(ClassName, $"type property not found for {subElement.ToString()}");

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs:46

  • ProcessFavicons is a key public API for the plugin’s favicon logic. Adding XML doc comments explaining parameters (e.g., sqlQuery, patternPrefix) and behavior would help future maintainers.
    public static void ProcessFavicons(

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs:51

  • [nitpick] The new asynchronous startup (LoadBookmarksInBackgroundAsync) and file‐watch debouncing are critical flows but aren’t covered by tests. Consider adding unit or integration tests to verify initialization and debouncing behavior.
    private async Task LoadBookmarksInBackgroundAsync()

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (1)

48-48: The fire-and-forget pattern still lacks exception handling.

This is the same issue identified in the previous review. The background initialization task could fail silently, leaving the plugin uninitialized indefinitely.

Apply this fix to ensure initialization failures are logged:

-        _ = LoadBookmarksInBackgroundAsync();
+        _ = Task.Run(async () =>
+        {
+            try
+            {
+                await LoadBookmarksInBackgroundAsync();
+            }
+            catch (Exception ex)
+            {
+                _context.API.LogException(ClassName, "Failed to initialize bookmarks", ex);
+            }
+        });
🧹 Nitpick comments (2)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (2)

113-136: Optimize performance by avoiding unnecessary null filtering.

The current implementation creates null entries that are filtered out later. Consider using Where clause before Select to avoid unnecessary object creation.

Apply this diff to improve performance:

-        var results = _cachedBookmarks
-            .Select(c =>
-            {
-                var score = topResults ? 5 : BookmarkLoader.MatchProgram(c, param).Score;
-                if (!topResults && score <= 0)
-                    return null;
-
-                return new Result
-                {
-                    Title = c.Name,
-                    SubTitle = c.Url,
-                    IcoPath = c.FaviconPath, // Use the pre-validated path directly.
-                    Score = score,
-                    Action = _ =>
-                    {
-                        _context.API.OpenUrl(c.Url);
-                        return true;
-                    },
-                    ContextData = new BookmarkAttributes { Url = c.Url }
-                };
-            })
-            .Where(r => r != null);
+        var results = _cachedBookmarks
+            .Select(c => new { Bookmark = c, Score = topResults ? 5 : BookmarkLoader.MatchProgram(c, param).Score })
+            .Where(x => topResults || x.Score > 0)
+            .Select(x => new Result
+            {
+                Title = x.Bookmark.Name,
+                SubTitle = x.Bookmark.Url,
+                IcoPath = x.Bookmark.FaviconPath,
+                Score = x.Score,
+                Action = _ =>
+                {
+                    _context.API.OpenUrl(x.Bookmark.Url);
+                    return true;
+                },
+                ContextData = new BookmarkAttributes { Url = x.Bookmark.Url }
+            });

149-149: Improve filter comparison for better performance.

The string comparison with OrdinalIgnoreCase is unnecessarily complex for filename comparison where exact matching is typically sufficient.

Consider simplifying the comparison:

-        if (Watchers.Any(x => x.Path.Equals(directory, StringComparison.OrdinalIgnoreCase) && x.Filter == Path.GetFileName(path)))
+        if (Watchers.Any(x => x.Path == directory && x.Filter == Path.GetFileName(path)))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b3dd83 and 68f3f21.

📒 Files selected for processing (2)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (4 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs
🧰 Additional context used
🧠 Learnings (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (3)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3118
File: Flow.Launcher/ViewModel/MainViewModel.cs:1404-1413
Timestamp: 2024-12-08T21:12:12.060Z
Learning: In the `MainViewModel` class, the `_lastQuery` field is initialized in the constructor and is never null.
🔇 Additional comments (6)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (6)

27-35: LGTM: Well-designed concurrency control fields.

The addition of instance tracking, thread-safe initialization flag, semaphore for concurrent loading prevention, and debounce token source demonstrates good architectural improvements for addressing the performance issues mentioned in the PR.


51-81: Excellent async initialization with proper concurrency control.

The implementation correctly uses a semaphore to prevent concurrent loading operations, validates the cache directory, and pre-validates favicon paths to improve query performance. The thread-safe design with proper resource management is well done.


86-108: Good user experience improvement during initialization.

Providing immediate feedback to users when the plugin is still initializing is a significant improvement over silently returning no results. The fallback handling for missing translation keys is also well implemented.


169-190: Well-implemented debouncing mechanism with proper cancellation.

The debouncing logic correctly cancels previous delay tasks when new file changes occur, preventing excessive reload operations. The exception handling for TaskCanceledException is appropriate for the debouncing pattern.


197-214: Robust async reload implementation with proper error handling.

The method correctly resets the initialization state, clears cached data, and provides comprehensive exception logging. The conditional disposal of file watchers is a good design choice for different reload scenarios.


268-270: Proper cleanup of debounce resources in Dispose.

The cancellation and disposal of the debounce token source ensures proper resource cleanup when the plugin is disposed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (1)

48-48: Fix unhandled exceptions in fire-and-forget pattern.

The current implementation uses a fire-and-forget pattern that could hide initialization failures. If LoadBookmarksInBackgroundAsync throws an unhandled exception, it could crash the application or leave the plugin uninitialized indefinitely.

Apply the suggested fix from the previous review to wrap the call in proper exception handling.

🧹 Nitpick comments (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (1)

71-77: Consider async I/O for file existence checks.

The pre-validation of favicon paths uses synchronous File.Exists() calls in a loop, which could block the thread for a significant time if there are many bookmarks or if the file system is slow.

 // Pre-validate all icon paths once to avoid doing it on every query
-foreach (var bookmark in _cachedBookmarks)
-{
-    if (string.IsNullOrEmpty(bookmark.FaviconPath) || !File.Exists(bookmark.FaviconPath))
-    {
-        bookmark.FaviconPath = DefaultIconPath;
-    }
-}
+await Task.Run(() =>
+{
+    Parallel.ForEach(_cachedBookmarks, bookmark =>
+    {
+        if (string.IsNullOrEmpty(bookmark.FaviconPath) || !File.Exists(bookmark.FaviconPath))
+        {
+            bookmark.FaviconPath = DefaultIconPath;
+        }
+    });
+});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17aab46 and e59902c.

📒 Files selected for processing (1)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (5)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3672
File: Flow.Launcher/MainWindow.xaml.cs:244-247
Timestamp: 2025-06-08T14:12:21.348Z
Learning: In Flow.Launcher, App.NotifyIcon is created before MainWindow creation, so null checks for App.NotifyIcon are not necessary when accessing it from MainWindow code.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3672
File: Flow.Launcher/MainWindow.xaml.cs:318-318
Timestamp: 2025-06-08T14:12:12.842Z
Learning: In Flow.Launcher, the App.NotifyIcon static property is initialized in the App class before MainWindow creation, so null checks are not needed when accessing App.NotifyIcon in MainWindow lifecycle methods.
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024-11-03T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Report (PR)
🔇 Additional comments (3)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (3)

15-15: Excellent async refactoring implementation.

The transition from IPlugin to IAsyncPlugin is well-implemented with proper use of semaphores for thread-safe initialization. The background loading approach with the "initializing..." message (mentioned in PR objectives) should significantly improve user experience by keeping the UI responsive.

Also applies to: 37-50, 52-85


169-190: Potential resource leak in debounce cancellation handling.

The debouncing implementation correctly handles cancellation but has a subtle issue: if multiple rapid file changes occur, the old CancellationTokenSource might not be properly disposed before being replaced, leading to potential resource leaks over time.

 private static void OnBookmarkFileChanged(object sender, FileSystemEventArgs e)
 {
-    var oldCts = Interlocked.Exchange(ref _debounceTokenSource, new CancellationTokenSource());
-    oldCts?.Cancel();
-    oldCts?.Dispose();
+    var newCts = new CancellationTokenSource();
+    var oldCts = Interlocked.Exchange(ref _debounceTokenSource, newCts);
+    try
+    {
+        oldCts?.Cancel();
+    }
+    finally
+    {
+        oldCts?.Dispose();
+    }

-    var newCts = _debounceTokenSource;

Likely an incorrect or invalid review comment.


87-106: Fix potential deadlock in QueryAsync method.

There's a logical flaw in the async query implementation. When _isInitialized is false, the method correctly waits for the semaphore. However, if initialization is still running in LoadBookmarksInBackgroundAsync, this creates a potential deadlock since both methods try to acquire the same semaphore.

 public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
 {
     if (!_isInitialized)
     {
-        // If the list is not initialized, we need to wait for the list to be refreshed before querying
-        await _initializationSemaphore.WaitAsync(token);
-        try
-        {
-            return QueryResults(query);
-        }
-        finally
-        {
-            _initializationSemaphore.Release();
-        }
+        // Wait for initialization to complete without holding the semaphore
+        while (!_isInitialized)
+        {
+            await Task.Delay(50, token);
+        }
+        return QueryResults(query);
     }
     else
     {
         return QueryResults(query);
     }
 }

Likely an incorrect or invalid review comment.

@@ -101,13 +101,6 @@
FontSize="14"
Text="{DynamicResource flowlauncher_plugin_browserbookmark_guideMessage02}"
TextWrapping="WrapWithOverflow" />
<TextBlock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flowlauncher_plugin_browserbookmark_guideMessage03 string does not exist, so we should remove this

@Jack251970
Copy link
Member

Jack251970 commented Jul 12, 2025

@dcog989 From my observation, I noticed these two problems:

  1. Performance loss after code refactor to FaviconHelper & ChromiumBookmarkLoader & FirefoxBookmarkLoader files. Not sure if it is because the introduction of new NuGet package.

Original:
13:55:49.8850+08:00 - INFO - ChromiumBookmarkLoader.LoadBookmarks - Load 218 favicons cost <613ms>
13:55:50.3516+08:00 - INFO - FirefoxBookmarkLoaderBase.GetBookmarksFromPath - Load 262 favicons cost <407ms>
13:55:51.9970+08:00 - INFO - ChromiumBookmarkLoader.LoadBookmarks - Load 441 favicons cost <1566ms>

After refactor:
13:56:53.4670+08:00 - INFO - ChromiumBookmarkLoader.LoadBookmarks - Load 218 favicons cost <1611ms>
13:56:59.3311+08:00 - INFO - ChromiumBookmarkLoader.LoadBookmarks - Load 441 favicons cost <5831ms>

  1. Firefox is not found after code factor to FirefoxBookmarkLoader.GetProfileIniPath function.

@dcog989
Copy link
Contributor Author

dcog989 commented Jul 12, 2025

Thanks, looking at these now.

@dcog989
Copy link
Contributor Author

dcog989 commented Jul 12, 2025

Firefox is not found

What is the path to your profile, please?

@Jack251970
Copy link
Member

Firefox is not found

What is the path to your profile, please?

%AppData%\Roaming\Mozilla\Firefox\profiles.ini

@Jack251970
Copy link
Member

Jack251970 commented Jul 12, 2025

I think my profile is not parsed correctly and that function returns empty.

image

@Jack251970
Copy link
Member

Jack251970 commented Jul 12, 2025

This is the right path: %AppData%\Mozilla\Firefox\Profiles/af6pgbog.default-release-1749051093266\places.sqlite but the refactor function seems to get this placesPath (%AppData%\Mozilla\Firefox\Profiles\aforcpca.default\places.sqlite) which does not exist.

@Jack251970
Copy link
Member

And this is my profile file content:

[Install308046B0AF4A39CB]
Default=Profiles/af6pgbog.default-release-1749051093266
Locked=1

[Profile1]
Name=default-release
IsRelative=1
Path=Profiles/af6pgbog.default-release-1749051093266

[Profile0]
Name=default
IsRelative=1
Path=Profiles/aforcpca.default
Default=1

[General]
StartWithLastProfile=1
Version=2

[BackgroundTasksProfiles]
MozillaBackgroundTask-308046B0AF4A39CB-defaultagent=94mq3auk.MozillaBackgroundTask-308046B0AF4A39CB-defaultagent
MozillaBackgroundTask-308046B0AF4A39CB-backgroundupdate=dpdkts96.MozillaBackgroundTask-308046B0AF4A39CB-backgroundupdate

@dcog989
Copy link
Contributor Author

dcog989 commented Jul 12, 2025

  1. this was caused by switching to a single thread as one of my attempts to find the cause of CPU spike - will revert to parallel threading
  2. I had some confusion over how Firefox designates default profile - Default=1 vs Default=[profile name] - plus using firefox -p vs using the new Profiles menu option can cause issues. I've rewritten this so we should be good now.

Just need to do some testing and will send updates.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (1)

192-193: Remove redundant SqliteConnection.ClearPool call.

Since the connection uses Pooling=false, calling SqliteConnection.ClearPool(connection) is redundant and can be removed.

-                    SqliteConnection.ClearPool(connection);
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (1)

57-57: Consider handling potential exceptions from the background initialization task.

The fire-and-forget pattern could hide initialization failures. If LoadBookmarksInBackgroundAsync throws an unhandled exception, the plugin could remain uninitialized indefinitely.

-        _ = LoadBookmarksInBackgroundAsync();
+        _ = Task.Run(async () =>
+        {
+            try
+            {
+                await LoadBookmarksInBackgroundAsync();
+            }
+            catch (Exception ex)
+            {
+                _context.API.LogException(ClassName, "Failed to initialize bookmarks", ex);
+            }
+        });
🧹 Nitpick comments (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (1)

238-240: Potential race condition in token exchange.

While the Interlocked.Exchange prevents most race conditions, there's a small window where the old token source could be disposed while still being used in another thread.

Consider using a more robust pattern:

-        var oldCts = Interlocked.Exchange(ref _debounceTokenSource, new CancellationTokenSource());
-        oldCts?.Cancel();
-        oldCts?.Dispose();
+        var newCts = new CancellationTokenSource();
+        var oldCts = Interlocked.Exchange(ref _debounceTokenSource, newCts);
+        try
+        {
+            oldCts?.Cancel();
+        }
+        finally
+        {
+            oldCts?.Dispose();
+        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 850794e and 5f34031.

📒 Files selected for processing (32)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (4 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs (4 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ar.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/cs.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/da.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es-419.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/fr.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/he.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/it.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ja.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ko.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nb.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nl.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-br.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-pt.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ru.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sk.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/tr.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/vi.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-cn.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml (1 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (4 hunks)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs (6 hunks)
✅ Files skipped from review due to trivial changes (26)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/fr.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sr.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/it.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/vi.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ru.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es-419.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/da.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ar.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-br.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pt-pt.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/es.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nl.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ja.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-cn.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/de.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/sk.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/nb.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/cs.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/pl.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/zh-tw.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/ko.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/tr.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/he.xaml
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/uk-UA.xaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs
  • Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3664
File: Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:326-326
Timestamp: 2025-06-06T05:26:32.163Z
Learning: In the Firefox bookmark loader (FirefoxBookmarkLoader.cs), using hardcoded Windows line endings ("\r\n") for splitting profiles.ini content is acceptable and platform-agnostic line splitting is not required.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (3)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3664
File: Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:326-326
Timestamp: 2025-06-06T05:26:32.163Z
Learning: In the Firefox bookmark loader (FirefoxBookmarkLoader.cs), using hardcoded Windows line endings ("\r\n") for splitting profiles.ini content is acceptable and platform-agnostic line splitting is not required.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (3)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3279
File: Flow.Launcher/Helper/WallpaperPathRetrieval.cs:44-46
Timestamp: 2025-02-28T07:47:24.148Z
Learning: In Flow.Launcher's WallpaperPathRetrieval class, using a `using` statement with MemoryStream when loading images with BitmapImage does not work properly, even when using BitmapCacheOption.OnLoad. The stream needs to remain open while the bitmap is in use.
Learnt from: NoPlagiarism
PR: Flow-Launcher/Flow.Launcher#3104
File: Flow.Launcher.Plugin/Result.cs:73-74
Timestamp: 2024-12-02T07:37:46.838Z
Learning: In 'Result.cs', the user prefers not to add additional data URI validation in the 'IcoPath' property setter.
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (3)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3572
File: Flow.Launcher/App.xaml.cs:214-216
Timestamp: 2025-07-06T12:21:37.947Z
Learning: In Flow Launcher, the UpdatePluginManifestAsync method in PluginsManifest.cs already has comprehensive internal try-catch handling that logs exceptions and returns false on failure rather than throwing, making external try-catch wrappers unnecessary.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3664
File: Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs:326-326
Timestamp: 2025-06-06T05:26:32.163Z
Learning: In the Firefox bookmark loader (FirefoxBookmarkLoader.cs), using hardcoded Windows line endings ("\r\n") for splitting profiles.ini content is acceptable and platform-agnostic line splitting is not required.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3791
File: Flow.Launcher.Core/Plugin/PluginManager.cs:293-295
Timestamp: 2025-07-01T05:46:13.251Z
Learning: In Flow.Launcher.Core/Plugin/PluginManager.cs, when checking if a plugin is modified within the PluginManager class itself, prefer using the internal static PluginModified(string id) method directly rather than going through API.PluginModified() for better performance and architectural design.
🧬 Code Graph Analysis (1)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (4)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (4)
  • Main (15-352)
  • RegisterBrowserDataDirectory (202-227)
  • List (121-197)
  • List (302-329)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs (4)
  • LoadFaviconsFromDb (113-181)
  • List (24-24)
  • List (36-111)
  • List (189-195)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/CustomChromiumBookmarkLoader.cs (1)
  • List (18-18)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (3)
  • FaviconHelper (13-137)
  • ExecuteWithTempDb (17-45)
  • SaveBitmapData (104-136)
🪛 GitHub Check: Check Spelling
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs

[warning] 128-128:
Favicons is not a recognized word. (unrecognized-spelling)

🔇 Additional comments (15)
Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs (5)

29-31: Good improvement to file watching strategy.

Switching from per-file to directory-level monitoring is more efficient and will catch journal file updates that were previously missed.


67-78: Excellent error handling addition.

The JSON parsing is now properly wrapped in try-catch to prevent crashes from malformed bookmark files. The error logging provides good diagnostic information.


112-118: Good refinement of bookmark type handling.

Explicitly checking for "url" type and validating required properties prevents invalid bookmarks from being added. This is more robust than the previous default case.


156-160: GLOB pattern usage improves favicon matching.

The switch from LIKE to GLOB pattern with host-based matching is more precise and should improve favicon lookup accuracy.


176-182: Excellent duplicate prevention logic.

The ConcurrentDictionary usage with TryAdd efficiently prevents duplicate favicon saves while ensuring all bookmarks get the correct favicon path assigned.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs (5)

17-45: Well-implemented database optimization strategy.

The temporary database copy with indexing is a smart approach to improve query performance without modifying the original database. The exception handling and cleanup logic are robust.


47-52: Simple and effective SVG detection.

The SVG detection logic properly handles both plain SVG files and those with XML declarations. Limiting the check to the first 100 bytes is efficient.


54-102: Comprehensive SVG to PNG conversion.

The conversion logic properly handles SVG scaling to fit the target canvas and includes appropriate error handling. The 64x64 target size is reasonable for favicon display.


104-136: Enhanced image processing with proper error handling.

The combined SVG detection and raster image processing provides a complete solution. The resize-to-max logic ensures consistent favicon sizes while preserving aspect ratios.


58-61: No change needed: disposing MemoryStream after SKSvg.Load is safe

SkiaSharp’s SKSvg.Load(stream) fully reads and parses the SVG into memory, so the input stream can be disposed immediately. You can keep the existing code in
Plugins/Flow.Launcher.Plugin.BrowserBookmark/Helper/FaviconHelper.cs:58-61 as-is:

using var stream = new MemoryStream(svgData);
using var svg = new SKSvg();
svg.Load(stream);

Likely an incorrect or invalid review comment.

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs (5)

63-118: Excellent async initialization with proper concurrency control.

The semaphore usage prevents concurrent initialization operations, and the try-finally pattern ensures the plugin always reaches an initialized state even on failure. The favicon cache clearing is a good practice.


125-151: Smart periodic reload strategy for Firefox.

The double-checked locking pattern and periodic reload logic effectively addresses the Firefox file watching challenges mentioned in the PR objectives while avoiding excessive reloads.


154-168: Good user feedback during initialization.

Providing immediate feedback to users during the initialization process improves the user experience compared to showing no results or errors.


229-261: Well-implemented debounced file watching.

The debouncing mechanism with cancellation tokens effectively prevents excessive reloads while ensuring changes are eventually processed. The event filtering is efficient.


339-341: Proper cleanup of cancellation tokens.

The cancellation token disposal in the Dispose method prevents resource leaks and ensures proper cleanup.

@dcog989
Copy link
Contributor Author

dcog989 commented Jul 12, 2025

that was a lot more complex than I expected - issues with Firefox favicon updates, etc. I also had issues merging some intermediate commits, so they they may need reviewing / applying again. Sorry for the extra work.

However, it all seems to be working and performing well.

@dcog989
Copy link
Contributor Author

dcog989 commented Jul 12, 2025

I've just spotted a minor issue I want to fix. Please hold the next review.

@Jack251970 Jack251970 added this to the 2.0.0 milestone Jul 13, 2025
@dcog989
Copy link
Contributor Author

dcog989 commented Jul 13, 2025

The original plugin had many issues, including checking places.sqlite for updates as a trigger to refresh a complete refresh of the bookmarks / favicons. The places db is used for all navigation, not just bookmarks so that meant the plugin was recreating the entire cache constantly even if the bookmarks had not changed. We are smarter now.

So, I've now largely rewritten the entire plugin. I'm therefore closing this PR and will submit a new one once I'm entirely happy with it. It will be a complete refactor.

Before that, I will submit a much smaller PR that fixes only the '100% CPU' issue.

@dcog989 dcog989 closed this Jul 13, 2025
@dcog989 dcog989 deleted the BrowserBookmark-big-bug-fix branch July 13, 2025 18:14
@jjw24 jjw24 removed this from the 2.0.0 milestone Jul 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working Code Refactor enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants